Conversation
ameba23
marked this pull request as draft
May 20, 2026 09:38
…tation doc in tests
7 tasks
ameba23
marked this pull request as ready for review
June 11, 2026 09:31
samlaf
added a commit
to SeismicSystems/attested-tls
that referenced
this pull request
Aug 31, 2026
AttestationResult sat at the boundary that is meant to hide which platform produced the evidence, and two of its three fields were Intel types: a dcap-qvl Quote and a QuoteCollateralV3. The AWS Nitro work in flashbots#45 has neither, so the struct would break a second time the moment a non-TDX platform lands. flashbots#65 breaks it with no new platform at all - it moves the collateral into the evidence, so the verifier fetches none. The parsed quote leaves the shared type. It had exactly one consumer: AttestationVerifier binds it only to hand to the GCP provenance check, which reads the PPID out of the PCK leaf. gcp/firmware.rs discards it, and so does our own downstream. dcap.rs still hands it back, now as the second element of a tuple, so nothing re-parses the same bytes (34346c6) and the module gains no public type of its own - flashbots#40 may replace that module with attest-verify, which already owns a ValidatedDcapQuote one letter away. CollateralSnapshot becomes EndorsementSnapshot, and the bundle becomes an optional field on it. What a verifier fetches varies; the instant it holds that material to does not. So the instant stays a plain field and the fetched material hangs off it, with #[non_exhaustive] and a ::dcap() constructor keeping the pairing structural and later fields additive. A struct rather than an enum keyed by platform, because a verification consumes a set of fetched material rather than one of several alternatives, and because the axis it varies along is not the platform: whether endorsements ride in the evidence or get fetched is a transport choice of the protocol. flashbots#49 already made that choice one way for the Azure AK chain, and flashbots#65 proposes the opposite for DCAP collateral. "Collateral" is Intel's DCAP word, and would read as a category error the moment the snapshot holds Azure vTPM roots or whatever Nitro needs. "Endorsements" is the loose umbrella rather than RFC 9334's strict term - a DCAP bundle spans Endorsements and Reference Values both - and the doc comment says so, so the imprecision is deliberate rather than sloppy. The type is called VerifiedAttestation again, which reverses the rename in the commit before this one. That rename argued the value is the far end of one appraisal. It is not: the appraisal policy runs after the value is built, and a caller can configure it to check nothing, which is exactly what our enclave does before appraising in its own admission predicate. So no Reference Value comparison is implied, and what comes back is verified evidence rather than an Attestation Result. Leaving the RFC's name unspent keeps it for the type that would earn it if appraisal is ever separated from verification. Result is also taken in Rust, with AttestationError in this same crate. The docs on both types are shorter for it. The cache-refresh caveat is stated once instead of three times, and where the value sits in the RATS pipeline is stated once, linked to RFC 9334. Addresses review feedback on flashbots#85. BREAKING CHANGE: AttestationResult is renamed VerifiedAttestation and loses its quote field. CollateralSnapshot is renamed EndorsementSnapshot, is #[non_exhaustive], and its collateral field becomes dcap: Option<QuoteCollateralV3>, built through EndorsementSnapshot::dcap. The dcap::verify_* functions return (VerifiedAttestation, Quote), the azure::verify_* functions return VerifiedAttestation, and AttestationVerifier::verify_attestation{,_sync} return Option<VerifiedAttestation>.
samlaf
added a commit
to SeismicSystems/attested-tls
that referenced
this pull request
Aug 31, 2026
AttestationResult sat at the boundary that is meant to hide which platform produced the evidence, and two of its three fields were Intel types: a dcap-qvl Quote and a QuoteCollateralV3. The AWS Nitro work in flashbots#45 has neither, so the struct would break a second time the moment a non-TDX platform lands. flashbots#65 breaks it with no new platform at all - it moves the collateral into the evidence, so the verifier fetches none. The parsed quote leaves the shared type. It had exactly one consumer: AttestationVerifier binds it only to hand to the GCP provenance check, which reads the PPID out of the PCK leaf. gcp/firmware.rs discards it, and so does our own downstream. dcap.rs still hands it back, now as the second element of a tuple, so nothing re-parses the same bytes (34346c6) and the module gains no public type of its own - flashbots#40 may replace that module with attest-verify, which already owns a ValidatedDcapQuote one letter away. CollateralSnapshot becomes EndorsementSnapshot, and the bundle becomes an optional field on it. What a verifier fetches varies; the instant it holds that material to does not. So the instant stays a plain field and the fetched material hangs off it, with #[non_exhaustive] and a ::dcap() constructor keeping the pairing structural and later fields additive. A struct rather than an enum keyed by platform, because a verification consumes a set of fetched material rather than one of several alternatives, and because the axis it varies along is not the platform: whether endorsements ride in the evidence or get fetched is a transport choice of the protocol. flashbots#49 already made that choice one way for the Azure AK chain, and flashbots#65 proposes the opposite for DCAP collateral. "Collateral" is Intel's DCAP word, and would read as a category error the moment the snapshot holds Azure vTPM roots or whatever Nitro needs. "Endorsements" is the loose umbrella rather than RFC 9334's strict term - a DCAP bundle spans Endorsements and Reference Values both - and the doc comment says so, so the imprecision is deliberate rather than sloppy. The type is called VerifiedAttestation again, which reverses the rename in the commit before this one. That rename argued the value is the far end of one appraisal. It is not: the appraisal policy runs after the value is built, and a caller can configure it to check nothing, which is exactly what our enclave does before appraising in its own admission predicate. So no Reference Value comparison is implied, and what comes back is verified evidence rather than an Attestation Result. Leaving the RFC's name unspent keeps it for the type that would earn it if appraisal is ever separated from verification. Result is also taken in Rust, with AttestationError in this same crate. The docs on both types are shorter for it. The cache-refresh caveat is stated once instead of three times, and where the value sits in the RATS pipeline is stated once, linked to RFC 9334. Addresses review feedback on flashbots#85. BREAKING CHANGE: AttestationResult is renamed VerifiedAttestation and loses its quote field. CollateralSnapshot is renamed EndorsementSnapshot, is #[non_exhaustive], and its collateral field becomes dcap: Option<QuoteCollateralV3>, built through EndorsementSnapshot::dcap. The dcap::verify_* functions return (VerifiedAttestation, Quote), the azure::verify_* functions return VerifiedAttestation, and AttestationVerifier::verify_attestation{,_sync} return Option<VerifiedAttestation>.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds AWS Nitro attestation support to attested-tls.
It adds a new attestation type
AwsNitrowhichAttestationGeneratorandAttestationVerifierwill use to create and verify Nitro attestation documents.The implementation uses the
nsm-nitro-enclave-utilscrate for NSM interaction and COSE/CBOR parsing.The AWS root certificate is hardcoded as DER. It will expire on 2049-10-28 and was retrieved from https://aws-nitro-enclaves.amazonaws.com/AWS_NitroEnclaves_Root-G1.zip
Attestation detection now checks for Nitro by attempting communication with the Nitro Security Module at
/dev/nsm. This is checked before attempting TDX DCAP attestation and only when thenitrofeature is present.For running tests outside of a Nitro enclave, mock Nitro attestations are produced which verify against a mock root of trust rather than AWS.
Nitro support is gated behind the
nitrofeature flag. But it is enabled by default. Happy to disable it by default is others prefer.Timestamp checks
Nitro attestation docs have a signed timestamp field. We can verify that this matches up with the not before / not after times on the TLS cert. In attested-tls crate, the not-before time is read immediately before attestation is generated (so that it can be included as input).
I have not yet added this check here, because it would be an API breaking change, needing to pass in a time-window to the attestation verifier fn. But if we were serious about adopting this, i would add it in a followup PR.
Testing on a deployment
For testing on a Nitro deployment, this pairs with flashbots/attested-tls-proxy#162 which adds vsocket support to
attestation-provider-serverallowing us to check that these nitro attestation documents will verify.There is also a flake for reproducibly building a docker container with
attestation-provider-server: https://github.com/flashbots/attested-tls-proxy/blob/peg/nitro-test/flake.nixHeres how to deploy using that flake (from the attested-tls-proxy repo on the paired branch):
This produces a Docker image tarball at result in the repo root.
The flake bakes in the enclave startup command, so the container starts the server in vsock mode and with
--server-attestation-type aws-nitroby default. That means you do not need to pass extra runtime flags when building the EIF.The server side then listens on vsock port 8000, and the parent host can connect with the client command using:
Which should verify the attestation-document, and dump it to a local file. But not actually check measurements.
This shows that nitro attestations can be verified but does not demonstrate an attested-tls workflow with nitro. flashbots/attested-tls-proxy#163 adds full vsocket support to attested-tls-proxy allowing us to test a full attested-tls workflow on Nitro.